﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;
using VideoLink.Frames;

namespace VideoLink.Streams
{
    /// <summary>
    ///  Источник видео, воспроизводящий его с помощью DirectShow,
    ///  передающий поток покадрово.
    /// </summary>
    public class ByFrameDSSource : VideoSourceBase, IDisposable
    {
        #region Fields

        /// <summary>
        /// Указатель на последний снятый кадр.
        /// Состояние в произвольный момент времени не определено.
        /// Не должен использоваться нигде, кроме GetFrameData.
        /// </summary>
        private IntPtr pointerToLastFrame;
        /// <summary>
        /// Показывает, необходимо ли удалять видео.
        /// Видео обычно удаляется при переходе в состояние Stop,
        /// но не должно удаляться, если этот переход вызван окончанием видео.
        /// </summary>
        private bool needDispose = true;

        /// <summary>
        /// Компонент, отвечающий за воспроизведение видео.
        /// </summary>
        protected DxPlay.DxPlay video;
        /// <summary>
        /// Контрол, на который отображается видео.
        /// </summary>
        protected Canvas canvas;

        /// <summary>
        /// Требование дизайнера
        /// </summary>
        private System.ComponentModel.IContainer components;
        
        #endregion

        /// <summary>
        /// Создать новый экземпляр класса по умолчанию
        /// </summary>
        public ByFrameDSSource()
            : base()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Создать новый экземпляр класса с указанными параметрами
        /// </summary>
        /// <param name="streamID">
        /// ID потока, уникален для всех потоков
        /// </param>
        /// <param name="fileName">
        /// Имя файла, который нужно воспроизвести и передать
        /// </param>
        public ByFrameDSSource(int streamID, string fileName)
            : base(streamID)
        {
            InitializeComponent();
            FileName = fileName;
        }

        /// <summary>
        /// Деструктор
        /// </summary>
        ~ByFrameDSSource()
        {
            DisposeVideo();
        }

        /// <summary>
        /// Подготовить поток к закрытию (осободить все ресурсы).
        /// </summary>
        public override void Close()
        {
            base.Close();

            DisposeVideo();
        }

        #region Component

        /// <summary>
        /// Дизайнер
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.canvas = new Canvas();
            this.SuspendLayout();
            //
            // canvas
            //
            this.ViewportPanel.Controls.Add(canvas);
            this.canvas.Dock = System.Windows.Forms.DockStyle.Fill;
            // 
            // ByFrameJPEGSource
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.Name = "ByFrameJPEGSource";
            this.ResumeLayout(false);

        }

        /// <summary>
        /// Подготовиться к воспроизведению указанного файла
        /// </summary>
        /// <param name="fileName">Файл для воспроизведения</param>
        private void InitializeVideo(string fileName)
        {
            video = new DxPlay.DxPlay(canvas, fileName);
            video.StopPlay += new DxPlay.DxPlay.DxPlayEvent(video_StopPlay);
        }

        /// <summary>
        /// Удалить текущее видео и освободить все ресурсы, с ним связанные
        /// </summary>
        protected void DisposeVideo()
        {
            if (video == null)
                return;
            video.StopPlay -= new DxPlay.DxPlay.DxPlayEvent(video_StopPlay);
            video.Stop();
            video.Dispose();
            video = null;
        }

        #endregion

        #region Playback

        /// <summary>
        /// Получить данные, соответствующие данному кадру.
        /// </summary>
        /// <returns>
        /// Текущее состояние потока.
        /// Если воспроизведение в данный момент идёт или State только что изменилось, 
        /// то используется OneFrame, иначе возвращается null
        /// </returns>
        public override VideoStreamState GetFrameData()
        {
            VideoStreamState result = null;
            if ((State == PlaybackState.Play) || (needSendData))
            {
                var bmp = new Bitmap(1, 1);
                if (IntPtr.Zero != pointerToLastFrame)
                    Marshal.FreeCoTaskMem(pointerToLastFrame);
                try
                {
                    pointerToLastFrame = video.SnapShot();
                    bmp = video.IPToBmp(pointerToLastFrame);
                }
                catch
                {
                    Marshal.FreeCoTaskMem(pointerToLastFrame);
                }

                result = new VideoStreamState(StreamID,
                    State,
                    new OneFrame(bmp));

                //если состояние не play, то надо послать состояние
                //один раз и успокоиться
                needSendData = State == PlaybackState.Play;
            }

            return result;
        }

        /// <summary>
        /// Вызывается при изменении свойства State в значение Pause
        /// или при нажатии кнопки "Pause" на контроле.
        /// Выполняются все действия, связанные с приостановкой воспроизведения
        /// (в данной реализации - создаётся видео).
        /// </summary>
        /// <param name="sender">-</param>
        /// <param name="e">-</param>
        protected override void OnPause(object sender, EventArgs e)
        {
            if (State == PlaybackState.Pause)
                return;

            base.OnPause(sender, e);

            if (video != null)
                video.Pause();
        }

        /// <summary>
        /// Вызывается при изменении свойства State в значение Play
        /// или при нажатии кнопки "Play" на контроле.
        /// Выполняются все действия, связанные с запуском воспроизведения
        /// </summary>
        /// <param name="sender">-</param>
        /// <param name="e">-</param>
        protected override void OnPlay(object sender, EventArgs e)
        {
            if (State == PlaybackState.Play)
                return;

            base.OnPlay(sender, e);

            if (video == null)
                InitializeVideo(FileName);
            video.Start();
        }

        /// <summary>
        /// Вызывается при изменении свойства State в значение Stop
        /// или при нажатии кнопки "Stop" на контроле.
        /// Выполняются все действия, связанные с остановкой воспроизведения
        /// (в данной реализации - освобождается видео).
        /// </summary>
        /// <param name="sender">-</param>
        /// <param name="e">-</param>
        protected override void OnStop(object sender, EventArgs e)
        {
            if (State == PlaybackState.Stop)
                return;

            base.OnStop(sender, e);

            if ((video != null) && needDispose)
                DisposeVideo();
        }

        /// <summary>
        /// Вызывается компонентом video при достижении конца видео
        /// в процессе воспроизведения
        /// </summary>
        /// <param name="sender">-</param>
        private void video_StopPlay(object sender)
        {
            needDispose = false;
            State = PlaybackState.Stop;
            needDispose = true;
            video.Rewind();
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Освободить все ресурсы и подготовить компонент к удалению
        /// </summary>
        void IDisposable.Dispose()
        {
            DisposeVideo();
        }

        #endregion
    }
}
